<?php

declare(strict_types=1);

namespace App\Chat\Service;

use App\Chat\Constant\TalkModeConstant;
use App\Chat\Service\Message\FormatMessageService;
use App\Common\Constants\GenderConstants;
use App\Common\Helpers\BcMath;
use App\Common\Robot\Robot;
use App\CustHub\Constants\MsgTypeConstants;
use App\CustHub\Mapper\ChContactMapper;
use App\CustHub\Mapper\ChCustomerServiceMapper;
use App\CustHub\Mapper\ChRoomMapper;
use App\CustHub\Model\ChContact;
use App\CustHub\Model\ChContactCorpTag;
use App\CustHub\Model\ChCustomerService;
use App\CustHub\Model\ChCustomerServiceRobot;
use App\CustHub\Model\ChRobot;
use App\CustHub\Model\ChRoom;
use App\CustHub\Model\ChTalkRecord;
use App\CustHub\Model\ChTalkSession;
use App\CustHub\Service\ContactReceiveCallBackService;
use App\Robot\Service\RobotApiService;
use Carbon\Carbon;
use FriendsOfHyperf\Lock\Exception\LockTimeoutException;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Mine\Abstracts\AbstractService;
use Mine\Exception\NormalStatusException;
use Mine\MineCollection;
use Mine\MineResponse;
use Nette\Utils\Random;
use Psr\Http\Message\ResponseInterface;
use function FriendsOfHyperf\Lock\lock;
use function Hyperf\Collection\collect;
use function Hyperf\Collection\data_get;

class TalkService extends AbstractService
{
    #[Inject]
    protected TalkSessionService $talkSessionService;

    #[Inject]
    protected TalkMessageService $talkMessageService;

    #[Inject]
    protected ChCustomerServiceMapper $customerServiceMapper;

    #[Inject]
    protected ChContactMapper $chContactMapper;

    #[Inject]
    protected ChRoomMapper $roomMapper;

    public function list(array $params)
    {
        $mainUid = $params['main_uid'];
        $keyword = $params['keyword'] ?? '';
        $csId = $params['cs_id'] ?? '';
        $robot_id = $params['robot_id'] ?? '';
        $nickname = $params['nickname'] ?? '';
        $corpTag = data_get($params, 'corp_tag', '');
        $corpTag = !is_array($corpTag)?json_decode($corpTag, true):$corpTag;
        $smartTag = data_get($params, 'smart_tag', '');
        $smartTag = !is_array($smartTag)?json_decode($smartTag, true):$smartTag;
        $pageSize = $params['page_size'] ?? 20;

        $startTime = $params['start_time'] ?? '';
        $endTime = $params['end_time'] ?? '';
        $tempTableName = null;
        try {
            // 处理企业标签
            $corpTagResult = $this->processTagFilter($mainUid, $corpTag, 'corp');
            $tagContactIds = $this->mergeTagResults($corpTagResult['include'], $corpTagResult['exclude']);
            // 处理智能标签
            $smartTagResult = $this->processTagFilter($mainUid, $smartTag, 'smart');
            $smartTagContactIds = $this->mergeTagResults($smartTagResult['include'], $smartTagResult['exclude']);

            $contactIds = $this->mergeFinalContactIds($tagContactIds, $smartTagContactIds);
            if((!empty($corpTag) || !empty($smartTag)) && empty($contactIds)){
                return [
                    'items' => [],
                    'pageInfo' => [
                        'total' => 0,
                        'currentPage' => 1,
                        'totalPage' => 1
                    ]
                ];
            }
            $query = Db::table('ch_talk_session as a')
                ->selectRaw('a.id,a.user_id as cs_uid,a.talk_type,a.robot_id,b.user_id,b.nickname,b.avatar,d.nickname AS robot_nickname,d.avatar AS robot_avatar,d.corp_name as robot_corp_name,b.gender,
  a.user_id AS cs_id,c.NAME AS cs_name,b.mobile_list,b.remark,a.cs_num,a.contact_num')
                ->join('ch_contact as b',function ($join){
                    $join->on('a.robot_id', '=', 'b.robot_id')
                        ->on('a.receiver_id','=','b.user_id');
                })
                ->join('ch_customer_service as c','a.user_id','=','c.id')
                ->join('ch_robot as d','d.id','=','a.robot_id')
                ->where('a.main_uid',$mainUid)
                ->where('a.talk_type',TalkModeConstant::PRIVATE_CHAT)
                ->when($csId,function ($query) use ($csId){
                    $query->where('a.user_id',$csId);
                });

            if (!empty($contactIds)) {
                list($query, $tempTableName) = $this->useTempTableForContactIds($query, $contactIds);
            }

            $rst = $query->when($robot_id,function ($query) use ($robot_id){
                $query->where('a.robot_id',$robot_id);
            })
            ->when($nickname,function ($query) use ($nickname){
                $query->where('b.nickname','like',"%{$nickname}%");
            })
            ->when($keyword,function ($query) use ($keyword){
                $query->where('e.text','like',"%{$keyword}%");
            })
            ->when($startTime && $endTime, function ($query) use ($startTime,$endTime) {
                $query->whereBetween('a.created_at', [$startTime, $endTime]);
            })
            ->groupBy('a.id')
            ->orderBy('a.id','desc')
            ->paginate((int)$pageSize);

            $data = $this->customerServiceMapper->setPaginate($rst);



            $items = $data['items'];
            foreach ($items as $k=>$item){
                $items[$k]->total_num = BcMath::add($item->cs_num,$item->contact_num,0);
                $items[$k]->gender_text = GenderConstants::getMessage($item->gender);
                $items[$k]->mobile_list = empty($item->mobile_list) ? [] : json_decode($item->mobile_list,true);
            }
            $data['items'] = $items;
            return $data;
        }catch (\Throwable){
           throw new NormalStatusException('获取会话列表失败');
        }finally {
            // 确保临时表被清理
            if ($tempTableName) {
                $this->cleanupTempTable($tempTableName);
            }
        }
    }

    // 使用临时表处理大量contactIds - 返回查询对象和临时表名
    private function useTempTableForContactIds($query, $contactIds): array
    {
        if (empty($contactIds)) {
            return [$query, null];
        }

        // 如果contactIds数量不大，直接使用WHERE IN
        if (count($contactIds) <= 1000) {
            return [$query->whereIn('b.id', $contactIds), null];
        }

        // 创建临时表处理大量数据
        $tempTableName = 'temp_contact_ids_' . uniqid();

        try {
            // 创建临时表
            Db::statement("CREATE TEMPORARY TABLE {$tempTableName} (id INT PRIMARY KEY) ENGINE=MEMORY");

            // 分块插入数据
            $chunks = array_chunk($contactIds, 1000);
            foreach ($chunks as $chunk) {
                $values = implode('), (', $chunk);
                Db::statement("INSERT INTO {$tempTableName} (id) VALUES ({$values})");
            }

            // 使用JOIN替代WHERE IN
            $query = $query->join("{$tempTableName} as temp", 'b.id', '=', 'temp.id');

            return [$query, $tempTableName];
        } catch (\Throwable $e) {
            // 如果创建临时表失败，尝试回退到WHERE IN
            if ($tempTableName) {
                $this->cleanupTempTable($tempTableName);
            }

            // 回退到使用WHERE IN，即使数据量大
            return [$query->whereIn('b.id', $contactIds), null];
        }
    }

    private function cleanupTempTable($tempTableName): void
    {
        try {
            Db::statement("DROP TEMPORARY TABLE IF EXISTS {$tempTableName}");
        } catch (\Throwable $e) {
            logger()->warning("删除临时表失败: {$tempTableName}, 错误: " . $e->getMessage());
        }
    }

    private function processTagFilter($mainUid, $tagConfig, $tagType)
    {
        if (empty($tagConfig)) {
            return ['include' => [], 'exclude' => []];
        }

        $includeMatchType = data_get($tagConfig, 'include_match_type', 0);
        $includeTagIds = data_get($tagConfig, 'include_tag_ids', []);
        $excludeSwitch = data_get($tagConfig, 'exclude_switch', 0);
        $excludeMatchType = data_get($tagConfig, 'exclude_match_type', 0);
        $excludeTagIds = data_get($tagConfig, 'exclude_tag_ids', []);

        // 确定表名和字段名
        $tagTable = $tagType === 'corp' ? 'ch_contact_corp_tag' : 'ch_contact_smart_tag';
        $tagIdField = $tagType === 'corp' ? 'corp_tag_id' : 'smart_tag_id';

        // 处理包含逻辑
        $includeContactIds = $this->getIncludeContactIds(
            $mainUid, $includeTagIds, $includeMatchType, $tagTable, $tagIdField
        );

        // 处理排除逻辑
        $excludeContactIds = [];
        if ($excludeSwitch) {
            $excludeContactIds = $this->getExcludeContactIds(
                $mainUid, $excludeTagIds, $excludeMatchType, $tagTable, $tagIdField
            );
        }

        return ['include' => $includeContactIds, 'exclude' => $excludeContactIds];
    }

    // 获取包含标签的联系人ID
    private function getIncludeContactIds($mainUid, $tagIds, $matchType, $tagTable, $tagIdField)
    {
        if (empty($tagIds)) {
            return [];
        }

        $query = Db::table("{$tagTable} as a")
            ->join('ch_contact as b', 'a.contact_id', '=', 'b.id')
            ->where('b.main_uid', $mainUid);

        switch ($matchType) {
            case 0: // 满足任意一个标签
                return $query->whereIn("a.{$tagIdField}", $tagIds)
                    ->distinct()
                    ->pluck('a.contact_id')
                    ->toArray();

            case 1: // 满足所有标签
                return $query->whereIn("a.{$tagIdField}", $tagIds)
                    ->groupBy('a.contact_id')
                    ->havingRaw('COUNT(DISTINCT a.'.$tagIdField.') = ?', [count($tagIds)])
                    ->pluck('a.contact_id')
                    ->toArray();

            default: // 未打标签
                return Db::table('ch_contact as b')
                    ->leftJoin("{$tagTable} as a", 'a.contact_id', '=', 'b.id')
                    ->where('b.main_uid', $mainUid)
                    ->whereNull('a.contact_id')
                    ->pluck('b.id')
                    ->toArray();
        }
    }

    // 获取排除标签的联系人ID
    private function getExcludeContactIds($mainUid, $tagIds, $matchType, $tagTable, $tagIdField)
    {
        if (empty($tagIds)) {
            return [];
        }

        switch ($matchType) {
            case 0: // 满足任意一个标签
                return Db::table("{$tagTable} as a")
                    ->join('ch_contact as b', 'a.contact_id', '=', 'b.id')
                    ->where('b.main_uid', $mainUid)
                    ->whereIn("a.{$tagIdField}", $tagIds)
                    ->distinct()
                    ->pluck('a.contact_id')
                    ->toArray();

            case 1: // 满足所有标签
                return Db::table("{$tagTable} as a")
                    ->join('ch_contact as b', 'a.contact_id', '=', 'b.id')
                    ->where('b.main_uid', $mainUid)
                    ->whereIn("a.{$tagIdField}", $tagIds)
                    ->groupBy('a.contact_id')
                    ->havingRaw('COUNT(DISTINCT a.'.$tagIdField.') = ?', [count($tagIds)])
                    ->pluck('a.contact_id')
                    ->toArray();

            default: // 已设置标签的
                return Db::table('ch_contact as b')
                    ->leftJoin("{$tagTable} as a", 'a.contact_id', '=', 'b.id')
                    ->where('b.main_uid', $mainUid)
                    ->whereNotNull('a.contact_id')
                    ->pluck('b.id')
                    ->toArray();
        }
    }

    private function mergeTagResults($includeContactIds, $excludeContactIds)
    {
        if (!empty($includeContactIds) && empty($excludeContactIds)) {
            return $includeContactIds;
        }

        if (empty($includeContactIds) && !empty($excludeContactIds)) {
            return $excludeContactIds;
        }

        if (!empty($includeContactIds) && !empty($excludeContactIds)) {
            return array_intersect($includeContactIds, $excludeContactIds);
        }

        return [];
    }

    private function mergeFinalContactIds($tagContactIds, $smartTagContactIds)
    {
        if (!empty($tagContactIds) && !empty($smartTagContactIds)) {
            // 使用array_intersect_key优化大数组交集计算
            $tagMap = array_flip($tagContactIds);
            $smartMap = array_flip($smartTagContactIds);

            $result = [];
            foreach ($tagMap as $id => $key) {
                if (isset($smartMap[$id])) {
                    $result[] = $id;
                }
            }
            return $result;
        } elseif (!empty($tagContactIds)) {
            return $tagContactIds;
        } elseif (!empty($smartTagContactIds)) {
            return $smartTagContactIds;
        } else {
            return [];
        }
    }

    public function getChatRecords(int $robotId, mixed $receiver_id, int $talk_type, int $record_id = null,int $limit = null,array $msg_type = [],string $keyword = ''): array
    {
        $fields = [
            'ch_talk_records.id',
            'ch_talk_records.talk_type',
            'ch_talk_records.msg_type',
            'ch_talk_records.user_id',
            'ch_talk_records.receiver as receiver_id',
            'ch_talk_records.is_revoke',
            'ch_talk_records.is_need_verify',
            'ch_talk_records.need_verify_msg',
            'ch_talk_records.content',
            'ch_talk_records.quote_id',
            'ch_talk_records.created_at',
        ];

        if($talk_type == TalkModeConstant::PRIVATE_CHAT){
            $fields = array_merge($fields,[
                \Hyperf\DbConnection\Db::raw('IF(ch_talk_records.is_cs = 1, ch_robot.avatar, ch_contact.avatar) as avatar'),
                \Hyperf\DbConnection\Db::raw('IF(ch_talk_records.is_cs = 1, ch_robot.nickname, ch_contact.nickname) as nickname')
            ]);
        }else{
            $fields = array_merge($fields, [
                \Hyperf\DbConnection\Db::raw('IF(ch_talk_records.is_cs = 1, ch_robot.avatar, ch_room_member.avatar) as avatar'),
                \Hyperf\DbConnection\Db::raw('IF(ch_talk_records.is_cs = 1, ch_robot.nickname, ch_room_member.nickname) as nickname')
            ]);
        }
        
        $model = ChTalkRecord::select($fields);
        $model->leftJoin('ch_robot', function ($join) {
            $join->on('ch_talk_records.robot_id', '=', 'ch_robot.id')
                ->where('ch_talk_records.is_cs', 1);
        });


       if($talk_type == TalkModeConstant::PRIVATE_CHAT){
           $model->leftJoin('ch_contact', function ($join) {
               $join->on('ch_talk_records.sender', '=', 'ch_contact.user_id')
                   ->whereRaw('ch_talk_records.robot_id = ch_contact.robot_id')
                   ->where('ch_talk_records.is_cs', 0);
           });
       }else{
           $model->leftJoin('ch_room_member',function ($join){
               $join->on('ch_room_member.room_wxid','=','ch_talk_records.from_room_id')
                   ->on('ch_room_member.user_id','=','ch_talk_records.sender')
                   ->where('ch_talk_records.is_cs', 0);
           });
       }

        $model->where('ch_talk_records.robot_id', $robotId);

        if(!empty($keyword)){
            $model->where('ch_talk_records.text', 'like', "%{$keyword}%");
        }

        if ($record_id) {
            $model->where('ch_talk_records.id', '<', $record_id);
        }

        if ($talk_type == TalkModeConstant::PRIVATE_CHAT) {
            $model->where(function ($query) use ($receiver_id) {
                $query->where([
                    ['ch_talk_records.receiver', '=', $receiver_id]
                ])->orWhere([
                    ['ch_talk_records.sender', '=', $receiver_id]
                ]);
            });
        } else {
            $model->where('ch_talk_records.from_room_id', '=', $receiver_id);
        }

        $model->where('ch_talk_records.talk_type', $talk_type);

        if ($msg_type) {
            $model->whereIn('ch_talk_records.msg_type', $msg_type);
        }

        if(!is_null($limit)){
            $model->limit($limit);
        }

        $rows = $model->orderBy('ch_talk_records.id','desc')->get()->toArray();

        $quoteIds = collect($rows)->filter(function ($item){
            return !empty($item['quote_id']);
        })->pluck('quote_id')->toArray();
        $quoteContents = [];
        if(!empty($quoteIds)){
            $quoteContents = $this->getQuoteContent($quoteIds,$talk_type);
        }

        foreach ($rows as $k=>$item){
            $rows[$k]['quote_content'] = $quoteContents[$item['quote_id']] ?? [];
        }

////        $keyword = empty($keyword) ? '*' : $keyword;
//        $rows =  $this->getDataByScout($keyword,$robotId,$talk_type,$record_id,$receiver_id,$msg_type,$limit);
        $data =  di()->get(FormatMessageService::class)->handleChatRecords($rows);
        $data = collect($data)->sortBy('id')->values()->all();
        return $data;
    }


    public function getQuoteContent($ids,$talk_type)
    {
        $fields = [
            'ch_talk_records.id',
            'ch_talk_records.talk_type',
            'ch_talk_records.msg_type',
            'ch_talk_records.user_id',
            'ch_talk_records.receiver as receiver_id',
            'ch_talk_records.is_revoke',
            'ch_talk_records.is_need_verify',
            'ch_talk_records.need_verify_msg',
            'ch_talk_records.content',
            'ch_talk_records.quote_id',
            'ch_talk_records.created_at',
        ];

        if($talk_type == TalkModeConstant::PRIVATE_CHAT){
            $fields = array_merge($fields,[
                \Hyperf\DbConnection\Db::raw('IF(ch_talk_records.is_cs = 1, ch_robot.avatar, ch_contact.avatar) as avatar'),
                \Hyperf\DbConnection\Db::raw('IF(ch_talk_records.is_cs = 1, ch_robot.nickname, ch_contact.nickname) as nickname')
            ]);
        }else{
            $fields = array_merge($fields, [
                \Hyperf\DbConnection\Db::raw('IF(ch_talk_records.is_cs = 1, ch_robot.avatar, ch_room_member.avatar) as avatar'),
                \Hyperf\DbConnection\Db::raw('IF(ch_talk_records.is_cs = 1, ch_robot.nickname, ch_room_member.nickname) as nickname')
            ]);
        }

        $model = ChTalkRecord::select($fields);
        $model->leftJoin('ch_robot', function ($join) {
            $join->on('ch_talk_records.robot_id', '=', 'ch_robot.id')
                ->where('ch_talk_records.is_cs', 1);
        });


        if($talk_type == TalkModeConstant::PRIVATE_CHAT){
            $model->leftJoin('ch_contact', function ($join) {
                $join->on('ch_talk_records.sender', '=', 'ch_contact.user_id')
                    ->whereRaw('ch_talk_records.robot_id = ch_contact.robot_id')
                    ->where('ch_talk_records.is_cs', 0);
            });
        }else{
            $model->leftJoin('ch_room_member',function ($join){
                $join->on('ch_room_member.room_wxid','=','ch_talk_records.from_room_id')
                    ->on('ch_room_member.user_id','=','ch_talk_records.sender')
                    ->where('ch_talk_records.is_cs', 0);
            });
        }

        $model->whereIn('ch_talk_records.id', $ids);
        return $model->get()->groupBy('id')->toArray();
    }


    private function getDataByScout($query,$robot_id,$talk_type,$record_id,$receiver_id,$msg_type,$limit): array
    {
        $fields = [
            'ch_talk_records.id',
            'ch_talk_records.talk_type',
            'ch_talk_records.msg_type',
            'ch_talk_records.user_id',
            'ch_talk_records.receiver as receiver_id',
            'ch_talk_records.is_revoke',
            'ch_talk_records.content',
            'ch_talk_records.created_at',
            \Hyperf\DbConnection\Db::raw('IF(ch_talk_records.is_cs = 1, ch_robot.avatar, ch_contact.avatar) as avatar'),
            \Hyperf\DbConnection\Db::raw('IF(ch_talk_records.is_cs = 1, ch_robot.nickname, ch_contact.nickname) as nickname')
        ];
        return ChTalkRecord::search($query,function ($client,$query,$params) use ($robot_id,$talk_type,$record_id,$receiver_id,$msg_type,$limit){
            $must = [];
            if (!empty($query)) {
                $must[] = [
                    'multi_match' => [
                        'query'  => $query,
                        'fields' => ['text^3', 'content.content^2','content.file_name^1'],
                        'type'   => 'best_fields',
                    ]
                ];
            }

            $params['body']['query'] = [
                'bool' => [
                    'must'   => $must,
                    'filter' => [
                        ['term' => ['robot_id' => (int)$robot_id]],
                        ['term' => ['talk_type' => (int)$talk_type]],
                    ]
                ]
            ];

            // 添加接收者条件
            if ($talk_type == TalkModeConstant::PRIVATE_CHAT) {
                $params['body']['query']['bool']['filter'][] = [
                    'bool' => [
                        'should' => [
                            ['term' => ['receiver' => $receiver_id]],
                            ['term' => ['sender' => $receiver_id]],
                        ]
                    ]
                ];
            } else {
                $params['body']['query']['bool']['filter'][] = [
                    'term' => ['from_room_id' => $receiver_id]
                ];
            }

            if(!empty($record_id)){
                $params['body']['query']['bool']['filter'][] = [
                    'range' => ['id' => ['lt' => $record_id]]
                ];
            }

            if (!empty($msg_type)) {
                $params['body']['query']['bool']['filter'][] = [
                    'terms' => ['msg_type' => $msg_type]
                ];
            }
            return $client->search($params);
        })->query(function ($query) use ($fields){
            $query->select($fields)->leftJoin('ch_robot', function ($join) {
                $join->on('ch_talk_records.robot_id', '=', 'ch_robot.id')
                    ->where('ch_talk_records.is_cs', 1);
            })
                ->leftJoin('ch_contact', function ($join) {
                    $join->on('ch_talk_records.sender', '=', 'ch_contact.user_id')
                        ->whereRaw('ch_talk_records.robot_id = ch_contact.robot_id')
                        ->where('ch_talk_records.is_cs', 0);
                });
        })
            ->take($limit)
            ->orderBy('id','desc')
            ->get()
            ->toArray();
    }

    public function export(array $params, ?string $dto, ?string $filename = null, ?\Closure $callbackData = null): ResponseInterface
    {
        if (empty($dto)) {
            return container()->get(MineResponse::class)->error('导出未指定DTO');
        }

        if (empty($filename)) {
            $filename = "聊天记录".Carbon::now()->toDateTimeString();
        }

        $info = ChTalkSession::query()
            ->where('id',$params['id'])
            ->first();
        if(empty($info)){
            return container()->get(MineResponse::class)->error('请选择您要导出的会话');
        }

        $result = $this->getChatRecords(
            $info->robot_id,
            $info->receiver_id,
            $info->talk_type
        );

        $data = [];
        foreach ($result as $key=>$item){
            switch ($item['msg_type']){
                case MsgTypeConstants::URL:
                    $content =  data_get($item,'content.content.url','');
                    break;
                case MsgTypeConstants::MINI_PROGRAM:
                case MsgTypeConstants::CUSTOM_MINI_PROGRAM:
                    $content = data_get($item,'content.content.title','') . data_get($item,'content.content.pagepath','');
                    break;
                case MsgTypeConstants::VIDEO_CHANNEL:
                    $content =  data_get($item,'content.content.channel_name','');
                    break;
                default:
                    $content = $item['content']['content'] ?? '';
                    break;
            }
            $data[] = [
                'nickname' => $item['nickname'],
                'receiver_id' => $item['receiver_id'],
                'msg_type' => MsgTypeConstants::getMessage($item['msg_type']),
                'content' => $content,
                'created_at' => $item['created_at']
            ];
        }

        return (new MineCollection())->export($dto, $filename, $data, $callbackData);
    }

    public function saveChatRecord(ChRobot $robot,array $data)
    {
        //是否群聊
        $isRoom = $data['is_room'];
        $talkType = $isRoom ? TalkModeConstant::GROUP_CHAT : TalkModeConstant::PRIVATE_CHAT;
        $isRobot = $data['is_robot'];
        $robotId = $robot->id;

        $msgId = $data['msg_server_id'] ?? ($data['msg_id'] ?? 0);
        $msg_unique_identifier = $data['msg_unique_identifier'] ?? ($data['appinfo'] ?? '');
        //判断是否客服发送，客服发送的情况，无须再次保存消息
        if(redis()->exists(ChTalkRecord::CUSTOMER_SERVICE_SENT.$msgId)){
            return;
        }

        if($isRoom){
            //群聊
            $receiveId = $data['from_room_id'];
            $roomInfo = $this->roomMapper->findByRoomWxid($receiveId,$robot->main_uid);
            if(empty($roomInfo)){
                return;
            }
        }else{
            //私聊
            $receiveId = $isRobot ? $data['receiver'] : $data['sender'];
            $info = $this->chContactMapper->findByRobotIdAndUserId($robot->id,$receiveId);
            if(empty($info)){
//                $lock = lock('talk_sync_contact_lock'.$robot->id.$receiveId,10);
//                try {
//                    $lock->block(8);
//                    $info = container()->get(ContactReceiveCallBackService::class)->saveContact($robot,$receiveId);
//                    if(empty($info)){
//                        return;
//                    }
//                } catch (LockTimeoutException $e) {
//                    return;
//                } finally {
//                    $lock->release();
//                }
                return;
            }
        }

        //获取会话信息
        $session = ChTalkSession::query()
            ->where('talk_type',$talkType)
            ->where('robot_id',$robotId)
            ->where('receiver_id',$receiveId)
            ->first();
        $isFirst = false;
        $sessionId = '';
        if($session){
            $sessionId = $session->id;
            if($session->is_hidden ==1 || $session->is_blocked == 1){
                //屏蔽或者拉黑的情况不接收用户发送的消息
                return;
            }
            //存在会话获取客服ID
            $customerServiceId = $session->user_id;
            $session->updated_at = Carbon::now();
            if($session->is_delete == 1){
                $session->is_delete = 0;
                $session->is_hidden = 0;
                $session->is_top = 0;
                $session->is_blocked = 0;
                $session->is_pending = 0;
                $session->is_disturb = 0;
            }

            $session->save();
        }else{
            //获取客服
            $customerService = ChCustomerService::query()
                ->where('status', 1)
                ->whereHas('robots',function ($query) use ($robotId){
                    $query->where('robot_id', $robotId);
                })
                ->get();
            if($customerService->isNotEmpty()){
                $customerService = $customerService->random(1)->first();
                $customerServiceId = $customerService->id;
                //todo 判断是否需要保存会话
                $this->talkSessionService->create($robot->main_uid,$robotId,$customerServiceId,$receiveId,$talkType,$isRobot);
                $isFirst = true;
            }else{
                //只记录发送记录，不保存会话
                $customerServiceId = 0;
                //不存在客服则不记录
                return;
            }
        }

        $msgType = $data['content']['msg_type'];
        $text = '';
        switch ($msgType){
            case MsgTypeConstants::TEXT:
                $data['content']['content'] = $text = htmlspecialchars($data['content']['content']) ;
                break;
            case MsgTypeConstants::IMAGE:
                $text = '图片消息';
                break;
            case MsgTypeConstants::VIDEO:
                $text = '视频消息';
                break;
            case MsgTypeConstants::FILE:
                $text = '文件消息';
                $data['content']['file_name'] = $data['msg_data']['filename'] ?? '';
                break;
            case MsgTypeConstants::MINI_PROGRAM:
                $text = '小程序消息';
                $msgData = $data['msg_data'];
                $content = [
                    'title' => $msgData['title'],
                    'desc'   => $msgData['desc'],
                    'appid' => $msgData['appid'] ?? $msgData['app_id'],
                    'username' => $msgData['username'],
                    'pagepath' => $msgData['pagepath'] ?? $msgData['page_path'],
                    'icon_url' => $msgData['icon_url'],
                    'cover_url' => ''
                ];
                $data['content'] = [
                    'content' => $content,
                    'msg_type' => 'custom_mini_program'
                ];
                break;
            case MsgTypeConstants::URL:
                $text = 'URL消息';
                $msgData = $data['msg_data'];
                $content = [
                    'title' => $msgData['title'],
                    'desc'   => $msgData['desc'],
                    'url' => $msgData['link_url'],
                    'cover' => $msgData['icon_url']
                ];
                $data['content'] = [
                    'content' => $content,
                    'msg_type' => MsgTypeConstants::URL
                ];
                break;
        }
        $quoteId = 0;
        if($msgType == MsgTypeConstants::TEXT || $msgType == MsgTypeConstants::MIXED_TEXT){
            if(!empty($data['msg_data']['reply']['msg_id'])){
                $quoteId = ChTalkRecord::query()
                    ->where('msg_unique_identifier',$data['msg_data']['reply']['msg_id'])
                    ->value('id');
            }
        }


        $content = [
            'main_uid'     => $robot->main_uid,
            'talk_type'    => $talkType,
            'robot_id'     => $robotId,
            'msg_type'     => $msgType,
            'msg_data'     => [
                'msgtype' => $data['msgtype'],
                'msg_data' => $data['msg_data']
            ],
            'user_id'      => $customerServiceId,
            'from_room_id' => $data['from_room_id'],
            'sender'       => $data['sender'],
            'receiver'     => $data['receiver'],
            'content'      => $data['content'],
            'msg_id'       => $msgId,
            'msg_unique_identifier' => $msg_unique_identifier,
            'is_cs'        => $isRobot ? 1 : 0//是否客服 0:否 1:是
        ];

        if(!empty($quoteId)){
            $content['quote_id'] = $quoteId;
        }

        if($msgType == MsgTypeConstants::TEXT){
            $content['text'] = $text;
        }

        if(!empty($data['at_list'])){
            //@好友 数据
            $content['warn_users'] = $data['at_list'];
        }

        $this->talkMessageService->insert($content,$text,$isFirst,$sessionId);
    }
}